package main

import (
	"GoRestart/mail"
	"bytes"
	"fmt"
	"github.com/Unknwon/goconfig"
	log "github.com/YoungPioneers/blog4go"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var log_Config, config_FileName string
var mail_to, mail_from, mail_user, mail_pass, mail_smtp, mail_smtp_port, mail_tls string
var cfg *goconfig.ConfigFile

//初始化几个配置文件的路径
func init() {
	goRestart_path := GetCurrentDirectory()
	//日志的配置文件路径，默认当前程序目录下的log_config.xml文件
	log_Config = goRestart_path + "/log_config.xml"
	//godenycc配置文件路径，默认当前程序目录下的godenycc_config.xml文件
	config_FileName = goRestart_path + "/gorestart_config.ini"

}

//获取当前路径
func GetCurrentDirectory() string {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0])) //返回绝对路径  filepath.Dir(os.Args[0])去除最后一个元素的路径
	if err != nil {
		log.Error(err)
	}
	return strings.Replace(dir, "\\", "/", -1) //将\替换成/
}

/*func main() {
	// ---------------------------------------------加载日志配置文件---------------------------------------------
	err := log.NewWriterFromConfigAsFile(log_Config)
	if nil != err {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	defer log.Close()
	cfg = LoadConfig(config_FileName)
	mail_use_res, err := cfg.GetValue("MAIL", "MAIL_USE")
	if err != nil {
		log.Error("获知是否启用mail时出错")
	}
	if mail_use_res == "true" {
		mail_to, mail_from, mail_user, mail_pass, mail_smtp, mail_smtp_port, mail_tls = mail.GetMailConfig(cfg)
	}
	subject := "主题"
	message := "内容"

	mail.SendMail(subject, message, mail_to, mail_from, mail_user, mail_pass, mail_smtp, mail_smtp_port, mail_tls)
}*/

func main() {
	out := make(chan int)
	// ---------------------------------------------加载日志配置文件---------------------------------------------
	err := log.NewWriterFromConfigAsFile(log_Config)
	if nil != err {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	defer log.Close()

	// ---------------------------------------------加载gorestart_config.ini配置文件---------------------------------------------
	cfg = LoadConfig(config_FileName)

	//检测是否启用发送邮件
	mail_use_res, err := cfg.GetValue("MAIL", "MAIL_USE")
	if err != nil {
		log.Error("获知是否启用mail时出错")
	}
	if mail_use_res == "true" {
		mail_to, mail_from, mail_user, mail_pass, mail_smtp, mail_smtp_port, mail_tls = mail.GetMailConfig(cfg)
	}

	//获取要进行检查的sesction信息，就像配置文件中的web1，web2那样
	str, err := cfg.GetValue("DEFAULT", "WEBSERVICE")
	if err != nil {
		log.Error("错了")
		os.Exit(1)
	}
	arr := strings.Split(str, ",")
	//获取sesction的名称

	for _, v := range arr {
		//fmt.Println(k, "--->", v)
		//去除两端的空格
		v1 := strings.TrimSpace(v)
		//循环对每个section进行检测
		go ActionCheck(cfg, v1, mail_use_res)
	}

	//checkurl, curltimeout, respcode, checksleeptime, error_etry, stopcmd, stopsleeptime, startcmd, startsleeptime := ReadCheckPropterties(cfg, "web1")
	//CheckURL(checkurl, curltimeout, respcode, checksleeptime, error_etry, stopcmd, stopsleeptime, startcmd, startsleeptime)

	time.Sleep(6000 * time.Second)

	<-out
}

//加载配置文件
func LoadConfig(configname string) *goconfig.ConfigFile {
	cfg, err := goconfig.LoadConfigFile(configname)
	if err != nil {
		log.Error("读取配置文件失败")
		os.Exit(1)
	}
	log.Info("读取到配置文件：", configname)
	return cfg
}

// 执行：1、获取配置文件中的参数信息；2、开始检测
func ActionCheck(cfg *goconfig.ConfigFile, sesction string, mail_use_res string) {
	checkurl, curltimeout, respcode, checksleeptime, error_etry, stopcmd, stopsleeptime, startcmd, startsleeptime := ReadCheckPropterties(cfg, sesction)
	CheckURL(checkurl, curltimeout, respcode, checksleeptime, error_etry, stopcmd, stopsleeptime, startcmd, startsleeptime, sesction, mail_use_res)
}

//循环读取配置文件中的web检查配置
func ReadCheckPropterties(cfg *goconfig.ConfigFile,
	section string) (string, string, string, string, string, string, string, string, string) {
	//	假设要读取web1
	checkurl, err := cfg.GetValue(section, "checkurl")
	if err != nil {
		log.Error("读取section: ", section, " 的值：checkurl出错")
	}
	curltimeout, err := cfg.GetValue(section, "curltimeout")
	if err != nil {
		log.Error("读取section: ", section, " 的值：curltimeout")
	}

	respcode, err := cfg.GetValue(section, "respcode")
	if err != nil {
		log.Error("读取section: ", section, " 的值：respcode")
	}
	checksleeptime, err := cfg.GetValue(section, "checksleeptime")
	if err != nil {
		log.Error("读取section: ", section, " 的值：checksleeptime")
	}

	error_etry, err := cfg.GetValue(section, "error_etry")
	if err != nil {
		log.Error("读取section: ", section, " 的值：error_etry")
	}
	stopcmd, err := cfg.GetValue(section, "stopcmd")
	if err != nil {
		log.Error("读取section: ", section, " 的值：stopcmd")
	}
	stopsleeptime, err := cfg.GetValue(section, "stopsleeptime")
	if err != nil {
		log.Error("读取section: ", section, " 的值：stopsleeptime")
	}

	startcmd, err := cfg.GetValue(section, "startcmd")
	if err != nil {
		log.Error("读取section: ", section, " 的值：startcmd")
	}

	startsleeptime, err := cfg.GetValue(section, "startsleeptime")
	if err != nil {
		log.Error("读取section: ", section, " 的值：startsleeptime")
	}

	return checkurl, curltimeout, respcode, checksleeptime, error_etry, stopcmd, stopsleeptime, startcmd, startsleeptime

}

//执行状态检查
func CheckURL(checkURL string, curltimeout string, respcode string,
	checksleeptime string, error_etry string, stopcmd string,
	stopsleeptime string, startcmd string, startsleeptime string, sesction string, mail_use_res string) {
	//每隔几秒检查check一次
	checksleeptime_int, err := strconv.Atoi(checksleeptime)
	if err != nil {
		log.Error("checksleeptime转换为int类型出错")
	}

	error_etry_int, err := strconv.Atoi(error_etry)

	if err != nil {
		log.Error("error_etry转换为int类型出错")
	}

	//连续检查发生错误几次后准备重启应用
	var total_errors int
	total_errors = 0

	//curl -I -m 10 -o /dev/null -s -w %{http_code} http://127.0.0.1:8080
	//curl -I -o /dev/null -s -w %{http_code} -m 10 http://127.0.0.1:8080
	//var buf bytes.Buffer
	var buf bytes.Buffer
	buf.Reset()
	//buf.WriteString(`curl -I -m 10 -o /dev/null -s -w %{http_code} `)
	buf.WriteString(`curl -I -o /dev/null -s -w %{http_code} -m `)
	buf.WriteString(curltimeout)
	buf.WriteString(" ")
	buf.WriteString(checkURL)

	//curl命令拼接后的字符串
	shellcmd := buf.String()

	//将循环检查的次数转换为int类型

	//这里应该无限循环检查
	for {
		cmd := exec.Command("/bin/bash", "-c", shellcmd)
		bytes, err := cmd.Output()
		if err != nil {
			log.Error("-----执行curl命令出错-----")
			log.Error("curl命令是：", shellcmd)
		} /* else {
			log.Debug("执行curl命令成功")
		}*/
		resp := string(bytes) //通常是200或者404这类的返回值

		//如果返回值是200，则正常
		if resp == respcode {
			//等待几秒后,跳出此次循环，再次下一次检查
			log.Debug("应用服务:", sesction, "运行正常，状态值为：", resp)
			time.Sleep(time.Duration(checksleeptime_int) * time.Second)
			//如果检查正常了，就将total_errors 重置为0
			total_errors = 0
			continue

		} else { //	如果检查的结果不是200怎么办？
			//到这里已经确定检查的结果不正常，那就将总次数加1
			log.Debug("应用服务:", sesction, "运行异常，状态值为：", resp)
			total_errors = total_errors + 1

			//假如连续检查几次后一直是失败的，那么我就要打算进行重启该应用的操作了。
			if total_errors >= error_etry_int {
				log.Error("应用服务：", sesction, "故障，开始重新启动")
				//既然这里已经判断应用服务故障，打算重启了，重启之前发送邮件通知一下
				if mail_use_res == "true" {

					buf.Reset()
					buf.WriteString(`应用服务 `)
					buf.WriteString(sesction)
					buf.WriteString(`发生故障`)
					subject := buf.String()

					buf.Reset()
					buf.WriteString(`应用服务 `)
					buf.WriteString(sesction)
					buf.WriteString(`发生故障`)
					message := buf.String()

					mail.SendMail(subject, message, mail_to, mail_from, mail_user,
						mail_pass, mail_smtp, mail_smtp_port, mail_tls)
				}

				result := RestartApp(stopcmd, stopsleeptime, startcmd, startsleeptime)
				//重启操作如果为真，则继续开始检查操作
				if result == true {
					total_errors = 0
					log.Info("应用服务：", sesction, "重启成功")
					//既然这里已经重启成功了，那就发邮件告知一下
					if mail_use_res == "true" {

						buf.Reset()
						buf.WriteString(`应用服务 `)
						buf.WriteString(sesction)
						buf.WriteString(`重启成功`)
						subject := buf.String()

						buf.Reset()
						buf.WriteString(`应用服务 `)
						buf.WriteString(sesction)
						buf.WriteString(`重启成功`)
						message := buf.String()

						mail.SendMail(subject, message, mail_to, mail_from, mail_user,
							mail_pass, mail_smtp, mail_smtp_port, mail_tls)
					}
					continue
				} else {
					//如果重启成功为假，则退出，需要检查问题点
					//os.Exit(1)
					log.Info("重启服务：", sesction, "失败")
				}

			}
			//并且也等待相应的秒数之后，再次进行检查
			time.Sleep(time.Duration(checksleeptime_int) * time.Second)
			continue

		}

	}

}

//重启应用程序的操作
func RestartApp(stopcmd string, stopsleeptime string, startcmd string, startsleeptime string) bool {
	//停止后等待几秒钟？
	stopsleeptime_int, err := strconv.Atoi(stopsleeptime)
	if err != nil {
		log.Error("转换stopsleeptime错误")
	}

	//启动后等待几秒钟？
	startsleeptime_int, err := strconv.Atoi(startsleeptime)
	if err != nil {
		log.Error("转换startsleeptime错误")
	}

	//停止操作
	stop_cmd := exec.Command("/bin/bash", "-c", stopcmd)
	_, err1 := stop_cmd.Output()
	if err1 != nil {
		log.Error("-----停止应用时出错-----")
		log.Error("停止应用命令是：", stopcmd)
		return false
	} /*else {
		log.Debug("停止应用成功")
	}*/
	//等待几秒后准备启动
	time.Sleep(time.Duration(stopsleeptime_int) * time.Second)

	//启动操作
	start_cmd := exec.Command("/bin/bash", "-c", startcmd)
	_, err2 := start_cmd.Output()
	if err2 != nil {
		log.Error("-----启动应用时出错-----")
		log.Error("启动应用命令是：", startcmd)
		return false
	} else {
		//log.Debug("启动应用成功")
		//	能走到这里就算是成功了
		//然后启动之后，需要等待一些时间，毕竟有些应用启动很慢的
		time.Sleep(time.Duration(startsleeptime_int) * time.Second)
		return true
	}
}
